﻿
Imports System.Text
Imports std

Public Interface store
    Inherits ICloneable
    Sub clear()
    Function parse(ByVal s As String) As Boolean
    Function export() As String
    Function keyprefix() As String
    Function enableRead() As Boolean
    Function read(ByVal key As String, Optional ByVal storoom As storoomTalk = Nothing) As Boolean
    Function enableWrite() As Boolean
    Function write(ByVal key As String, Optional ByVal storoom As storoomTalk = Nothing) As Boolean
    Function enableUniqueWrite() As Boolean
    Function uniqueWrite(ByVal key As String, Optional ByVal storoom As storoomTalk = Nothing) As Boolean
    Function enableModify() As Boolean
    Function modify(ByVal key As String, Optional ByVal storoom As storoomTalk = Nothing) As Boolean
    Function enableCache() As Boolean
End Interface

Namespace storehelper
    Public Module storehelper
        Public Const prefixkeySeparator As Char = character.underline

        Private cache As cacheManager(Of String, String) = Nothing
        Private STOROOM_STRUCTURESTORE_CACHE_HIT_PERCENT As Int64 = npos

        Sub New()
            cache = New cacheManager(Of String, String)(True, 32 * 1024, cacheManager(Of String, String).notRetireTicks)
            STOROOM_STRUCTURESTORE_CACHE_HIT_PERCENT = _
                counter.registerAverageAndLastAverage("STOROOM_STRUCTURESTORE_CACHE_HIT_PERCENT", sampleRate:=0.1)
        End Sub

        Public Function generatekey(ByVal prefix As String, ByVal key As String) As String
            If Not isemptystring(prefix) Then
                Return prefix + prefixkeySeparator + key
            Else
                Return key
            End If
        End Function

        Public Function generatekey(ByVal obj As store, ByVal key As String) As String
            assert(Not obj Is Nothing, "obj is not an valid store instance.")
            Return generatekey(obj.keyprefix(), key)
        End Function

        Public Sub breakupkey(ByVal key As String, ByRef keyprefix As String, ByRef storekey As String)
            If Not strseg(key, keyprefix, storekey, prefixkeySeparator, True) Then
                keyprefix = Nothing
                copy(storekey, key)
            End If
        End Sub

        Public Sub clearCache()
            cache.clear()
        End Sub

        Friend Function read(ByVal obj As store, ByVal key As String, _
                             Optional ByVal storoom As storoomTalk = Nothing) As Boolean
            If storoom Is Nothing Then
                storoom = _storoom.storoom
            End If
            assert(Not obj Is Nothing, "obj is not an valid store instance.")
            If Not obj.enableRead() Then
                assert(False, "not enable read in " + obj.GetType.FullName)
            End If
            obj.clear()
            Dim s As String = Nothing
            Dim sk As String = Nothing
            sk = generatekey(obj, key)
            If Not (obj.enableCache() AndAlso cache.get(sk, s)) Then
                Dim buff As bytes = Nothing
                If storoom.read(sk, buff) AndAlso _
                   Not buff Is Nothing AndAlso buff.size() > 0 Then
                    s = encoding.utf8().GetString(buff)
                Else
                    raiseError(errorHandle.errorType.warning, "cannot read ", sk, " from storoom.")
                    Return False
                End If
                'if enableCache, but still need to read, that means cache cannot get the sk, so update the cache
                If obj.enableCache() Then
                    cache.add(sk, s)
                    counter.increase(STOROOM_STRUCTURESTORE_CACHE_HIT_PERCENT, 0)
                End If
            Else
                counter.increase(STOROOM_STRUCTURESTORE_CACHE_HIT_PERCENT, 100)
            End If
            Return obj.parse(s)
        End Function

        Private Delegate Function wm(ByVal key As String, ByVal buff As bytes) As Boolean

        Private Function wmwork(ByVal obj As store, ByVal key As String, ByVal func As wm, _
                                ByVal updateCache As Boolean) As Boolean
            assert(Not obj Is Nothing, "obj is not an valid store instance.")
            Dim s As String = Nothing
            s = obj.export()
            If Not s Is Nothing Then
                Dim sk As String = Nothing
                sk = generatekey(obj, key)
                If func(sk, encoding.utf8.GetBytes(s)) Then
                    If obj.enableCache() Then
                        'only updateCache when modify / uniqueWrite,
                        'when write, the input is just a part of the data, so erase it from cache
                        If updateCache Then
                            cache.add(sk, s)
                        Else
                            cache.erase(sk)
                        End If
                    End If
                    Return True
                Else
                    Return False
                End If
            Else
                raiseError("failed to export from object " + GetType(store).FullName, errorHandle.errorType.exclamation)
                Return False
            End If
        End Function

        Friend Function uniquewrite(ByVal obj As store, ByVal key As String, _
                                    Optional ByVal storoom As storoomTalk = Nothing) As Boolean
            If storoom Is Nothing Then
                storoom = _storoom.storoom
            End If
            assert(Not obj Is Nothing, "obj is not an valid store instance.")
            If Not obj.enableUniqueWrite() Then
                assert(False, "not enable uniqueWrite in " + obj.GetType.FullName)
            End If
            Return wmwork(obj, key, AddressOf storoom.uniqueWrite, True)
        End Function

        Friend Function write(ByVal obj As store, ByVal key As String, _
                              Optional ByVal storoom As storoomTalk = Nothing) As Boolean
            If storoom Is Nothing Then
                storoom = _storoom.storoom
            End If
            assert(Not obj Is Nothing, "obj is not an valid store instance.")
            If Not obj.enableWrite() Then
                assert(False, "not enable write in " + obj.GetType.FullName)
            End If
            Return wmwork(obj, key, AddressOf storoom.write, False)
        End Function

        Friend Function modify(ByVal obj As store, ByVal key As String, _
                              Optional ByVal storoom As storoomTalk = Nothing) As Boolean
            If storoom Is Nothing Then
                storoom = _storoom.storoom
            End If
            assert(Not obj Is Nothing, "obj is not an valid store instance.")
            If Not obj.enableModify() Then
                assert(False, "not enable modify in " + obj.GetType.FullName)
            End If
            Return wmwork(obj, key, AddressOf storoom.modify, True)
        End Function
    End Module
End Namespace

Public MustInherit Class list
    Inherits list(Of String)
    Implements store

    Public Overridable Function enableCache() As Boolean Implements store.enableCache
        Return False
    End Function

    Public Overridable Shadows Sub clear() Implements store.clear
        MyBase.clear()
    End Sub

    Private Function export() As String Implements store.export
        Return ToString(character.newline) + character.newline
    End Function

    'not a good hack here, to avoid break wfileis.data.info
    Protected Overridable Function enableEmptyEntity() As Boolean
        Return False
    End Function

    Private Function parse(ByVal s As String) As Boolean Implements store.parse
        'support an element to be nothing or empty string, so should not ignore empty entity
        'but some old data has empty entities, so just leave ignoreEmptyEntity as True
        'should use struct instead of list for some structure data
        strsepar(s, character.newline, Me, Not enableEmptyEntity())
        Return True
    End Function

    Public Function append(ByVal key As String, _
                           Optional ByVal storoom As storoomTalk = Nothing) As Boolean
        Dim origin As list = Nothing
        origin = alloc(Me)
        If origin.read(key, storoom) Then
            Dim other As list = Nothing
            copy(other, Me)
            Dim it As list.iterator = Nothing
            it = other.begin()
            While it <> other.end()
                If origin.find(+it) <> origin.end() Then
                    it = other.erase(it)
                Else
                    it += 1
                End If
            End While

            If other.size() > 0 Then
                Return other.write(key, storoom)
            Else
                Return True
            End If
        Else
            Return write(key, storoom)
        End If
    End Function

    Protected Function others(ByVal startIndex As Int64) As String
        Return ToString(startIndex, character.newline)
    End Function

    Protected MustOverride Function keyprefix() As String Implements store.keyprefix
    Protected MustOverride Function enableRead() As Boolean Implements store.enableRead
    Protected MustOverride Function enableWrite() As Boolean Implements store.enableWrite
    Protected MustOverride Function enableUniqueWrite() As Boolean Implements store.enableUniqueWrite
    Protected MustOverride Function enableModify() As Boolean Implements store.enableModify

    Public Function read(ByVal key As String, _
                         Optional ByVal storoom As storoomTalk = Nothing) As Boolean Implements store.read
        Return storehelper.read(Me, key, storoom)
    End Function

    Public Function write(ByVal key As String, _
                          Optional ByVal storoom As storoomTalk = Nothing) As Boolean Implements store.write
        Return storehelper.write(Me, key, storoom)
    End Function

    Public Function uniqueWrite(ByVal key As String, _
                                Optional ByVal storoom As storoomTalk = Nothing) As Boolean Implements store.uniqueWrite
        Return storehelper.uniquewrite(Me, key, storoom)
    End Function

    Public Function modify(ByVal key As String, _
                           Optional ByVal storoom As storoomTalk = Nothing) As Boolean Implements store.modify
        Return storehelper.modify(Me, key, storoom)
    End Function
End Class

Public MustInherit Class struct
    Inherits map(Of String, String)
    Implements store

    Public Const lineSeparator As Char = character.newline
    Public Const keyvalueSeparator As Char = character.tab
    '1. lineSeparator <newline char> as line separator
    '2. keyvalueSeparator <tab char> as key value separator
    '3. store unescaped data in m
    '4. escape when export
    '5. unescape when parse

    Public Overridable Function enableCache() As Boolean Implements store.enableCache
        Return False
    End Function

    Public Shadows Sub clear() Implements store.clear
        MyBase.clear()
    End Sub

    Protected MustOverride Function keyprefix() As String Implements store.keyprefix
    Protected MustOverride Function enableRead() As Boolean Implements store.enableRead
    Protected MustOverride Function enableWrite() As Boolean Implements store.enableWrite
    Protected MustOverride Function enableUniqueWrite() As Boolean Implements store.enableUniqueWrite
    Protected MustOverride Function enableModify() As Boolean Implements store.enableModify

    Private Function parse(ByVal s As String) As Boolean Implements store.parse
        Dim lines As vector(Of String) = Nothing
        strsepar(s, character.newline, lines, True)
        If Not lines Is Nothing AndAlso lines.size() > 0 Then
            Dim i As Int64
            For i = 0 To lines.size() - 1
                Dim index As Int64
                index = strindexof(lines(i), keyvalueSeparator, True)
                If index <> npos Then
                    data(unescape(strleft(lines(i), index))) = _
                         unescape(strmid(lines(i), index + strlen(keyvalueSeparator)))
                Else
                    insert(unescape(lines(i)), Nothing)
                End If
            Next
        End If

        Return True
    End Function

    Private Function export() As String Implements store.export
        Dim rtn As StringBuilder = Nothing
        rtn = New StringBuilder()
        Dim it As iterator = Nothing
        it = begin()
        While it <> [end]()
            Dim first As String = Nothing
            Dim second As String = Nothing
            first = (+it).first()
            second = (+it).second
            If Not isemptystring(first) Then
                rtn.Append(escape_unref(first))
                If Not isemptystring(second) Then
                    rtn.Append(keyvalueSeparator)
                    rtn.Append(escape_unref(second))
                End If
            End If
            it += 1
        End While

        Return Convert.ToString(rtn)
    End Function

    Public Function read(ByVal key As String, _
                         Optional ByVal storoom As storoomTalk = Nothing) As Boolean Implements store.read
        Return storehelper.read(Me, key, storoom)
    End Function

    Public Function write(ByVal key As String, _
                          Optional ByVal storoom As storoomTalk = Nothing) As Boolean Implements store.write
        Return storehelper.write(Me, key, storoom)
    End Function

    Public Function uniqueWrite(ByVal key As String, _
                                Optional ByVal storoom As storoomTalk = Nothing) As Boolean Implements store.uniqueWrite
        Return storehelper.uniquewrite(Me, key, storoom)
    End Function

    Public Function modify(ByVal key As String, _
                           Optional ByVal storoom As storoomTalk = Nothing) As Boolean Implements store.modify
        Return storehelper.modify(Me, key, storoom)
    End Function
End Class

Public MustInherit Class element
    Implements store

    Private d As String = Nothing

    Public Overridable Function enableCache() As Boolean Implements store.enableCache
        Return False
    End Function

    Public Overridable Sub clear() Implements store.clear
        d = Nothing
    End Sub

    Private Function export() As String Implements store.export
        Return d
    End Function

    Private Function parse(ByVal s As String) As Boolean Implements store.parse
        copy(s, d)
        Return True
    End Function

    Protected MustOverride Function keyprefix() As String Implements store.keyprefix
    Protected MustOverride Function enableRead() As Boolean Implements store.enableRead
    Protected MustOverride Function enableWrite() As Boolean Implements store.enableWrite
    Protected MustOverride Function enableUniqueWrite() As Boolean Implements store.enableUniqueWrite
    Protected MustOverride Function enableModify() As Boolean Implements store.enableModify

    Public Function read(ByVal key As String, _
                         Optional ByVal storoom As storoomTalk = Nothing) As Boolean Implements store.read
        Return storehelper.read(Me, key, storoom)
    End Function

    Public Function write(ByVal key As String, _
                          Optional ByVal storoom As storoomTalk = Nothing) As Boolean Implements store.write
        Return storehelper.write(Me, key, storoom)
    End Function

    Public Function uniqueWrite(ByVal key As String, _
                                Optional ByVal storoom As storoomTalk = Nothing) As Boolean Implements store.uniqueWrite
        Return storehelper.uniquewrite(Me, key, storoom)
    End Function

    Public Function modify(ByVal key As String, _
                           Optional ByVal storoom As storoomTalk = Nothing) As Boolean Implements store.modify
        Return storehelper.modify(Me, key, storoom)
    End Function

    Protected MustOverride Function generateInstance() As element

    Public Function Clone() As Object Implements ICloneable.Clone
        Dim rtn As element = Nothing
        rtn = generateInstance()
        copy(rtn.d, d)
        Return rtn
    End Function
End Class
